<?php
/**
 * Beaver_Model_Abstract
 * 
 * @category   Beaver
 * @package    Beaver
 * @subpackage Beaver_Model
 * @author     Mihály Kuprivecz
 *
 */
abstract class Beaver_Model_Abstract {
	
	/**
	 * @var array
	 */
	protected $_attributes = array ();
	
	/**
	 * @var Beaver_Model_Mapper
	 */
	protected $_mapper;
	
	/**
	 * Name of the Model
	 * @var string
	 */
	protected $_name;
	
	/**
	 * Array of the validators for the attributes
	 *
	 * @var array
	 */
	protected $_validate = array ();
	
	/**
	 * Continer of setted validator objects
	 *
	 * @var array
	 */
	protected $_validator = array ();
	
	/**
	 * Array of the required attributes
	 *
	 * @var array
	 */
	protected $_required = array ();
	
	/**
	 * The mapper set the model attributes automatically
	 * @var bolean
	 */
	protected $_attributesFromMapper = true;
	
	/**
	 * The mapper set the validators of model automatically
	 * It's deppend on the $_attributesFromMapper
	 * @var bolean
	 */
	protected $_validatorsFromMapper = true;
	
	/**
	 * Constructor
	 *
	 * @param  array|null $options
	 * @return void
	 */
	public function __construct(array $attributes = null, $mapperClass = null) {
		if (is_array ( $attributes )) {
			$this->setAttributes ( $attributes );
		}
		if (null !== $mapperClass) {
			$this->setMapper ( new $mapperClass ( ) );
		}
		// set the attributes from mapper
		if($this->_attributesFromMapper)
		{
			$this->getMapper()->addModelAttributesFromSource();
		}
		// set the validators from the defined validates
		$this->setValidatiors();
		$this->init();
	}
	
	/**
	 * Getter method, the mapper need to read this
	 *
	 * @return boolean
	 */
	public function getValidatorsFromMapper()
	{
		return $this->_validatorsFromMapper;
	} 
	
	/**
	 * Initialize the model
	 * @return void
	 */
	public function init(){
		
	}
	
	/**
	 * Overloading: allow property access
	 *
	 * @param  string $name
	 * @param  mixed $value
	 * @return void
	 */
	public function __set($name, $value) {
		if (! array_key_exists ( $name, $this->_attributes )) {
			throw new Beaver_Model_Exception ( "Invalid property specified in " . $this->getModelName () . " model" );
		}
		$this->_attributes [$name] = $value;
	}
	
	/**
	 * Overloading: allow property access
	 *
	 * @param  string $name
	 * @return mixed
	 */
	public function __get($name) {
		if (! array_key_exists ( $name, $this->_attributes )) {
			throw new Beaver_Model_Exception ( "Invalid property '" . $name . "' specified in " . $this->getModelName () . " model" );
		}
		return $this->_attributes [$name];
	}
	
	/**
	 * Set object state
	 *
	 * @param  array $options
	 * @return Beaver_Model_Abstract
	 */
	public function setAttributes(array $attributes) {
		foreach ( $attributes as $name => $value ) {
			if ( array_key_exists ( $name, $this->_attributes )) {
				$this->_attributes [$name] = $value;
			}
		}
		return $this;
	}
	
	/**
	 * Add add attribute to the model
	 *
	 * @param string $name
	 * @param mixed $value
	 * @param void
	 */
	public function addAttribute($name = null, $value = null) {
		if ($name !== null && is_string ( $name )) {
			$this->_attributes [$name] = $value;
		} else {
			throw new Beaver_Model_Exception ( "Try to add invalid property in " . $this->getModelName () . " model" );
		}
	}
	
	/**
	 * Clear all available attributes of the model
	 */
	public function clearAttributes()
	{
		$attributes = $this->getAttributes();
		foreach ( $attributes as $attribute => $value )
		{
			$this->$attribute = null;
		}
	}
	
	/**
	 * Get all available attributes of the model
	 *
	 * @return array
	 */
	public function getAttributes() {
		return $this->_attributes;
	}
	
	/**
	 * Set data mapper
	 *
	 * @param  mixed $mapper
	 * @return Beaver_Model_Abstract
	 */
	public function setMapper(Beaver_Model_Mapper_Interface $mapper) {
		$this->_mapper = $mapper;
		$this->_mapper->setModel ( $this );
		return $this;
	}
	
	/**
	 * Get data mapper
	 * Lazy loads Beaver_Model_Mapper_Interface instance if no mapper registered.
	 *
	 * @return Beaver_Model_Mapper
	 */
	public function getMapper() {
		if (null === $this->_mapper) {
			$class = "Default_Model_" . $this->getModelName () . "_Mapper";
			if (class_exists ( $class )) {
				$this->setMapper ( new $class ( $this ) );
			} else {
				$this->setMapper ( new Beaver_Model_Mapper_Db ( $this ) );
			}
		}
		return $this->_mapper;
	}
	
	/**
	 * Get the name of the extended Model
	 * @return string
	 */
	public function getModelName() {
		if (null === $this->_name) {
			$cut = strpos ( get_class ( $this ), "_Model_" ) + 7;
			$this->_name = substr ( get_class ( $this ), $cut );
		}
		return $this->_name;
	}
	
	/**
	 * Get validation criteria(s) on the model
	 *
	 * @param string $attribute
	 * @return array
	 */
	public function getValidations($attribute = null)
	{
		if (null === $attribute) return $this->_validate;
		if (isset($this->_validate[$attribute])) return $this->_validate[$attribute];
		return array();
	}
	/**
	 * Add validation criteria to the model
	 *
	 * @param string $attribute
	 * @param string $type
	 * @param array $parameters
	 */
	public function addValidation($attribute, $type, $message = null, $parameters = null)
	{
		if (!isset($this->_validate[$attribute])) $this->_validate[$attribute] = array();
		$validate = array("type" => $type);
		if(null !== $parameters) $validate["attributes"] = $parameters;
		if(null !== $message) $validate["message"] = $message;
		$this->_validate[$attribute][] = $validate;
	}
	/**
	 * Validate the model attribute(s)
	 *
	 * @param string $attribute
	 * @return array
	 */
	public function validate($attribute=null)
	{
		$attributes = $this->getAttributes();
		$validation = array();
		$error = false;
		if(null===$attribute) {
			foreach ($attributes as $name => $value) {

				if (!in_array($name,$this->_required) && ( empty($value) || null === $value) ) continue;
				
				if (in_array($name,$this->_required) && ( empty($value) || null === $value) )
				{
					$validation[$name]["valid"]    = false;
					$validation[$name]["errorMessages"] = array(Beaver_Language::getInstance()->get("Validator","Model_Required"));
					$error = true;
					continue;
				}
				
				if (isset($this->_validator[$name]) && $this->_validator[$name] instanceof Beaver_Validate ) {
					$validation[$name]["valid"]    = $this->_validator[$name]->isValid($value);
					$validation[$name]["errorMessages"] = $this->_validator[$name]->getMessages();
					if (count($validation[$name]["errorMessages"]) > 0) $error = true;
				}
			}
			if ($error)
			{
				return $validation;
			}
			return true;
		}
		else {
			$name  = $attribute;
			$value = $attributes[$name];
			
			if (!in_array($name,$this->_required) && ( empty($value) || null === $value) ) return true;
			if (in_array($name,$this->_required) && ( empty($value) || null === $value) )
			{
				$validation[$name]["valid"]    = false;
				$validation[$name]["errorMessages"] = array(Beaver_Language::getInstance()->get("Validator","Model_Required"));
				return $validation; 
			}
			
			if (isset($this->_validator[$name]) && $this->_validator[$name] instanceof Beaver_Validate ) {
				$validation["valid"]    = $this->_validator[$name]->isValid($value);
				$validation["errorMessages"] = $this->_validator[$name]->getMessages();
				return $validation;
			}
			return true;
		}
	}
	
	/**
	 * Add validation to the Model attribute
	 * @return void
	 */
	public function setValidatiors()
	{
		foreach($this->_validate as $attribute => $validators) {
			$this->_validator[$attribute] = new Beaver_Validate();
			// declare only the name of the validator
			if(is_string($validators))
			{
				$this->_validator[$attribute]->addValidatorWithParameters($validators);
			}
			// declare only one validator with parameters 
			if(is_array($validators))
			{
				$message = null;
				if(isset($validators["type"]))
				{
					if(isset($validators["message"])) $message = $validators["message"];
					$type = $validators["type"];
					$attributes = array();
					if(isset($validators["attributes"]) && is_array($validators["attributes"])) $attributes = $validators["attributes"];
					$this->_validator[$attribute]->addValidatorWithParameters($type,$attributes,$message);
				}
				else
				{
					//declare more validators
					foreach ($validators as $validator) {
						if(!isset($validator["type"]))
						{
							throw new Beaver_Model_Exception( "Invalid validation specified in " . $this->getModelName () . " model" );
						}
						if(isset($validator["message"])) $message = $validator["message"];
						$type = $validator["type"];
						$attributes = array();
						if(isset($validator["attributes"]) && is_array($validator["attributes"])) $attributes = $validator["attributes"];
						$this->_validator[$attribute]->addValidatorWithParameters($type,$attributes,$message);
					}
				}
			}
		}
	}
	
	public function setAttributeToRequired($attribute)
	{
		if (!in_array($attribute,$this->_required))
		{
			$this->_required[] = $attribute;
		}
	}
}